package cn.ldf.keepaccounts.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.ldf.keepaccounts.dao.RoomsDao;
import cn.ldf.keepaccounts.entity.dto.RoomsDTO;
import cn.ldf.keepaccounts.entity.dto.RoomsHistoryDTO;
import cn.ldf.keepaccounts.entity.event.MembersEvent;
import cn.ldf.keepaccounts.entity.event.RoomsEvent;
import cn.ldf.keepaccounts.entity.pojo.Members;
import cn.ldf.keepaccounts.entity.pojo.Rooms;
import cn.ldf.keepaccounts.entity.pojo.Users;
import cn.ldf.keepaccounts.entity.vo.DissolutionMemberOneVO;
import cn.ldf.keepaccounts.entity.vo.MembersAvatarVO;
import cn.ldf.keepaccounts.enums.RoomsStatusEnum;
import cn.ldf.keepaccounts.enums.WsOptionEnum;
import cn.ldf.keepaccounts.exception.ServerException;
import cn.ldf.keepaccounts.service.MembersService;
import cn.ldf.keepaccounts.service.RoomsService;
import cn.ldf.keepaccounts.service.UsersService;
import cn.ldf.keepaccounts.utils.ExceptionUtil;
import cn.ldf.keepaccounts.utils.StreamUtil;
import cn.ldf.keepaccounts.utils.WxRequest;
import cn.ldf.keepaccounts.websocket.RoomsWebsocket;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 房间表(Rooms)表服务实现类
 *
 * @author 清峰
 * @since 2025-02-17 10:15:14
 */
@Service("roomsService")
public class RoomsServiceImpl extends ServiceImpl<RoomsDao, Rooms> implements RoomsService {

    @Resource
    private RoomsDao roomsDao;
    @Resource
    private MembersService membersService;
    @Resource
    private UsersService usersService;
    @Resource
    private WxRequest wxRequest;
    @Resource
    private HttpServletResponse response;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RoomsDTO createRooms(RoomsDTO roomsDTO) {
        String ownerOpenId = roomsDTO.getRooms().getOwnerOpenId();
        Rooms one = this.lambdaQuery().eq(Rooms::getStatus, RoomsStatusEnum.DOING).eq(Rooms::getOwnerOpenId, ownerOpenId).one();
        ExceptionUtil.throwMsg(() -> ObjUtil.isNotEmpty(one), "该房主有未结算的房间，请勿重复创建");
        // 防止房间号重复
        List<String> roomsList = this.lambdaQuery().select(Rooms::getRoomId).list().stream().map(Rooms::getRoomId).toList();
        String roomId = checkIsExistRoomId(roomsList);
        //创建房间
        Rooms rooms = roomsDTO.getRooms();
        rooms.setRoomId(roomId);
        rooms.setStatus(RoomsStatusEnum.DOING);
        this.save(rooms);
        //插入房间成员
        List<Members> membersList = roomsDTO.getMembers().stream().map(x -> Members.builder().roomId(roomId).nickname(x.getNickname())
                .openId(x.getOpenId()).score(x.getScore()).settled(x.isSettled()).joinTime(new Date()).build()).toList();
        membersService.saveBatch(membersList);
        return roomsDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean dissolutionRooms(String roomId) {
        //查询房间是否已经结算
        Rooms rooms = checkRoomStatus(roomId);
        boolean updateRooms = this.lambdaUpdate()
                .set(Rooms::getStatus, RoomsStatusEnum.DONE).set(Rooms::getSettleTime, new Date())
                .eq(Rooms::getRoomId, roomId).update();
        if (updateRooms) {
            //获取成员信息
            List<Members> membersList = membersService.getNoSettledByRoomId(roomId);
            ExceptionUtil.throwMsg(() -> CollUtil.isEmpty(membersList), "根据房间号未查询到房间成员信息");
            //获取用户信息并进行积分赋值
            updateMembersAndUserScoreByMembers(membersList);
            //推送ws
            RoomsEvent roomsEvent = RoomsEvent.builder().wsOptionEnum(WsOptionEnum.DISSOLUTION_ROOMS).rooms(rooms).build();
            roomsEvent.setDissolutionRoom(Boolean.TRUE);
            RoomsWebsocket.sendInfo(roomsEvent, roomId);
        }
        return true;
    }

    @Override
    public DissolutionMemberOneVO dissolutionRoomsToMember(String roomId, String openId) {
        //查询房间是否已经结算
        checkRoomStatus(roomId);
        //查询该成员是否在房间中且未结算
        Members members = membersService.getByRoomIdAndOpenId(roomId, openId);
        ExceptionUtil.throwMsg(() -> ObjUtil.isEmpty(members), "房间成员不存在");
        ExceptionUtil.throwMsg(members::isSettled, "该成员已结算");
        //获取用户信息并进行积分赋值
        List<Members> membersList = Collections.singletonList(members);
        updateMembersAndUserScoreByMembers(membersList);
        //推送ws
        Users users = usersService.getByOpenId(openId);
        MembersEvent membersEvent = MembersEvent.builder().wsOptionEnum(WsOptionEnum.DISSOLUTION_ROOMS_MEMBER).membersList(membersList).users(users).build();
        RoomsWebsocket.sendInfo(membersEvent, roomId);
        return DissolutionMemberOneVO.builder()
                .roomId(roomId)
                .openId(openId)
                .isDissolutionSuccess(Boolean.TRUE)
                .score(members.getScore())
                .build();
    }

    private Rooms checkRoomStatus(String roomId) {
        Rooms rooms = this.getByRoomId(roomId);
        ExceptionUtil.throwMsg(() -> ObjUtil.isEmpty(rooms), "房间号不存在");
        ExceptionUtil.throwMsg(() -> RoomsStatusEnum.DONE.equals(rooms.getStatus()), "房间已经结算，无法再次结算");
        return rooms;
    }

    private void updateMembersAndUserScoreByMembers(List<Members> membersList) {
        List<String> openIdList = membersList.stream().map(Members::getOpenId).toList();
        List<Users> usersList = usersService.getListByOpenId(openIdList);
        membersList.forEach(members -> {
            //更新房间成员结算状态
            members.setSettled(Boolean.TRUE);
            membersService.updateSettledById(members.getId());
            //更新用户积分
            usersList.stream().filter(users -> members.getOpenId().equals(users.getOpenId())).forEach(users -> {
                users.setScore(users.getScore() + members.getScore());
                users.setGamesCount(users.getGamesCount() + 1);
                Integer winningCount = users.getWinningCount();
                users.setWinningCount(members.getScore() > 0 ? winningCount + 1 : winningCount);
                users.setWinningRate(NumberUtil.div(users.getWinningCount(), users.getGamesCount(), 2).doubleValue() * 100);
                usersService.updateById(users);
            });
        });
    }

    @Override
    public Rooms getByRoomId(String roomId) {
        return this.lambdaQuery().eq(Rooms::getRoomId, roomId).one();
    }

    @Override
    public Boolean isDissolutionRooms(String roomId) {
        ExceptionUtil.throwMsg(() -> !isExistByRoomId(roomId), "房间号不存在");
        return this.lambdaQuery().eq(Rooms::getRoomId, roomId).eq(Rooms::getStatus, RoomsStatusEnum.DONE).exists();
    }

    @Override
    public Boolean isExistByRoomId(String roomId) {
        return this.lambdaQuery().eq(Rooms::getRoomId, roomId).exists();
    }

    @Override
    public void generateQrCode(String roomId, String pagePath) {
        InputStream is = wxRequest.generateQrCode(roomId, pagePath);
        byte[] bytes = StreamUtil.readInputStream(is);
        try (ServletOutputStream os = response.getOutputStream()) {
            response.setContentType("image/png");
            response.setContentLength(bytes.length);
            os.write(bytes);
            os.flush();
        } catch (IOException e) {
            throw new ServerException("生成小程序二维码失败", e);
        }
    }

    @Override
    public Page<RoomsHistoryDTO> dissolutionHistory(Page<Rooms> page, Rooms rooms) {
        //根据rooms条件动态查询
        QueryWrapper<Rooms> roomsQueryWrapper = new QueryWrapper<>(rooms);
        roomsQueryWrapper.lambda().orderByDesc(Rooms::getSettleTime);
        Page<Rooms> roomsPage = this.page(page, roomsQueryWrapper);
        List<Rooms> roomsRecords = roomsPage.getRecords();
        if (CollUtil.isEmpty(roomsRecords)) {
            return null;
        }
        List<String> roomIds = roomsRecords.stream().map(Rooms::getRoomId).toList();
        List<Members> membersList = membersService.getListByRoomIds(roomIds);
        List<String> openIds = membersList.stream().filter(Objects::nonNull).map(Members::getOpenId).distinct().toList();
        List<Users> usersList = usersService.getListByOpenId(openIds);
        //获取房间成员信息
        List<MembersAvatarVO> membersAvatarVOList = membersList.stream().map(members -> {
            Users users = usersList.stream().filter(x -> members.getOpenId().equals(x.getOpenId())).findFirst().orElse(new Users());
            return MembersAvatarVO.builder().roomId(members.getRoomId()).openId(members.getOpenId())
                    .score(members.getScore()).nickname(users.getNickname()).avatar(users.getAvatar())
                    .contentType(users.getContentType()).build();
        }).toList();
        //封装每个房间成员信息
        List<RoomsHistoryDTO> roomsHistoryDTOList = roomsRecords.stream().map(roomsRecord -> {
            List<MembersAvatarVO> membersAvatars = membersAvatarVOList.stream().filter(x -> roomsRecord.getRoomId().equals(x.getRoomId())).toList();
            return RoomsHistoryDTO.builder().rooms(roomsRecord).membersAvatarVOList(membersAvatars).build();
        }).toList();
        Page<RoomsHistoryDTO> listPage = new Page<>(roomsPage.getCurrent(), roomsPage.getSize());
        listPage.setRecords(roomsHistoryDTOList);
        listPage.setPages(roomsPage.getPages());
        listPage.setTotal(roomsPage.getTotal());
        return listPage;
    }

    @Override
    public Rooms getDoingRoomByOpenId(String openId) {
        return roomsDao.getRoomByOpenIdAndStatus(openId, RoomsStatusEnum.DOING.name());
    }

    /**
     * 此方法用于确保每个房间都有一个唯一的ID，以避免房间重复
     *
     * @param roomsList 房间ID列表，用于检查生成的房间ID是否已存在
     * @return 返回一个唯一的房间ID
     */
    private static String checkIsExistRoomId(List<String> roomsList) {
        String roomId = RandomUtil.randomString(6);
        if (roomsList.contains(roomId)) {
            checkIsExistRoomId(roomsList);
        }
        return roomId;
    }
}

